/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */
package org.apache.xmlrpc.client;

import java.util.List;

import org.apache.xmlrpc.XmlRpcConfig;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.XmlRpcRequest;
import org.apache.xmlrpc.common.XmlRpcController;
import org.apache.xmlrpc.common.XmlRpcWorkerFactory;
import org.apache.xmlrpc.serializer.XmlWriterFactory;

/**
 * <p>
 * The main access point of an XML-RPC client. This object serves mainly as an
 * object factory. It is designed with singletons in mind: Basically, an
 * application should be able to hold a single instance of
 * <code>XmlRpcClient</code> in a static variable, unless you would be working
 * with different factories.
 * </p>
 * <p>
 * Until Apache XML-RPC 2.0, this object was used both as an object factory and
 * as a place, where configuration details (server URL, suggested encoding, user
 * credentials and the like) have been stored. In Apache XML-RPC 3.0, the
 * configuration details has been moved to the
 * {@link org.apache.xmlrpc.client.XmlRpcClientConfig} object. The configuration
 * object is designed for being passed through the actual worker methods.
 * </p>
 * <p>
 * A configured XmlRpcClient object is thread safe: In other words, the
 * suggested use is, that you configure the client using
 * {@link #setTransportFactory(XmlRpcTransportFactory)} and similar methods,
 * store it in a field and never modify it again. Without modifications, the
 * client may be used for an arbitrary number of concurrent requests.
 * </p>
 * 
 * @since 3.0
 */
public class XmlRpcClient extends XmlRpcController {
	private XmlRpcTransportFactory transportFactory = XmlRpcClientDefaults
			.newTransportFactory(this);
	private XmlRpcClientConfig config = XmlRpcClientDefaults
			.newXmlRpcClientConfig();
	private XmlWriterFactory xmlWriterFactory = XmlRpcClientDefaults
			.newXmlWriterFactory();

	protected XmlRpcWorkerFactory getDefaultXmlRpcWorkerFactory() {
		return new XmlRpcClientWorkerFactory(this);
	}

	/**
	 * Sets the clients default configuration. This configuration is used by the
	 * methods {@link #execute(String, List)},
	 * {@link #execute(String, Object[])}, and {@link #execute(XmlRpcRequest)}.
	 * You may overwrite this per request by using
	 * {@link #execute(XmlRpcClientConfig, String, List)}, or
	 * {@link #execute(XmlRpcClientConfig, String, Object[])}.
	 * 
	 * @param pConfig
	 *            The default request configuration.
	 */
	public void setConfig(XmlRpcClientConfig pConfig) {
		config = pConfig;
	}

	/**
	 * Returns the clients default configuration. This configuration is used by
	 * the methods {@link #execute(String, List)},
	 * {@link #execute(String, Object[])}. You may overwrite this per request by
	 * using {@link #execute(XmlRpcClientConfig, String, List)}, or
	 * {@link #execute(XmlRpcClientConfig, String, Object[])}.
	 * 
	 * @return The default request configuration.
	 */
	public XmlRpcConfig getConfig() {
		return config;
	}

	/**
	 * Returns the clients default configuration. Shortcut for
	 * <code>(XmlRpcClientConfig) getConfig()</code>. This configuration is used
	 * by the methods {@link #execute(String, List)},
	 * {@link #execute(String, Object[])}. You may overwrite this per request by
	 * using {@link #execute(XmlRpcClientConfig, String, List)}, or
	 * {@link #execute(XmlRpcClientConfig, String, Object[])}
	 * 
	 * @return The default request configuration.
	 */
	public XmlRpcClientConfig getClientConfig() {
		return config;
	}

	/**
	 * Sets the clients transport factory. The client will invoke the factory
	 * method {@link XmlRpcTransportFactory#getTransport()} for any request.
	 * 
	 * @param pFactory
	 *            The clients transport factory.
	 */
	public void setTransportFactory(XmlRpcTransportFactory pFactory) {
		transportFactory = pFactory;
	}

	/**
	 * Returns the clients transport factory. The client will use this factory
	 * for invocation of {@link XmlRpcTransportFactory#getTransport()} for any
	 * request.
	 * 
	 * @return The clients transport factory.
	 */
	public XmlRpcTransportFactory getTransportFactory() {
		return transportFactory;
	}

	/**
	 * Performs a request with the clients default configuration.
	 * 
	 * @param pMethodName
	 *            The method being performed.
	 * @param pParams
	 *            The parameters.
	 * @return The result object.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public Object execute(String pMethodName, Object[] pParams)
			throws XmlRpcException {
		return execute(getClientConfig(), pMethodName, pParams);
	}

	/**
	 * Performs a request with the given configuration.
	 * 
	 * @param pConfig
	 *            The request configuration.
	 * @param pMethodName
	 *            The method being performed.
	 * @param pParams
	 *            The parameters.
	 * @return The result object.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public Object execute(XmlRpcClientConfig pConfig, String pMethodName,
			Object[] pParams) throws XmlRpcException {
		return execute(new XmlRpcClientRequestImpl(pConfig, pMethodName,
				pParams));
	}

	/**
	 * Performs a request with the clients default configuration.
	 * 
	 * @param pMethodName
	 *            The method being performed.
	 * @param pParams
	 *            The parameters.
	 * @return The result object.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public Object execute(String pMethodName, List pParams)
			throws XmlRpcException {
		return execute(getClientConfig(), pMethodName, pParams);
	}

	/**
	 * Performs a request with the given configuration.
	 * 
	 * @param pConfig
	 *            The request configuration.
	 * @param pMethodName
	 *            The method being performed.
	 * @param pParams
	 *            The parameters.
	 * @return The result object.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public Object execute(XmlRpcClientConfig pConfig, String pMethodName,
			List pParams) throws XmlRpcException {
		return execute(new XmlRpcClientRequestImpl(pConfig, pMethodName,
				pParams));
	}

	/**
	 * Performs a request with the clients default configuration.
	 * 
	 * @param pRequest
	 *            The request being performed.
	 * @return The result object.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public Object execute(XmlRpcRequest pRequest) throws XmlRpcException {
		return getWorkerFactory().getWorker().execute(pRequest);
	}

	/**
	 * Performs an asynchronous request with the clients default configuration.
	 * 
	 * @param pMethodName
	 *            The method being performed.
	 * @param pParams
	 *            The parameters.
	 * @param pCallback
	 *            The callback being notified when the request is finished.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public void executeAsync(String pMethodName, Object[] pParams,
			AsyncCallback pCallback) throws XmlRpcException {
		executeAsync(getClientConfig(), pMethodName, pParams, pCallback);
	}

	/**
	 * Performs an asynchronous request with the given configuration.
	 * 
	 * @param pConfig
	 *            The request configuration.
	 * @param pMethodName
	 *            The method being performed.
	 * @param pParams
	 *            The parameters.
	 * @param pCallback
	 *            The callback being notified when the request is finished.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public void executeAsync(XmlRpcClientConfig pConfig, String pMethodName,
			Object[] pParams, AsyncCallback pCallback) throws XmlRpcException {
		executeAsync(
				new XmlRpcClientRequestImpl(pConfig, pMethodName, pParams),
				pCallback);
	}

	/**
	 * Performs an asynchronous request with the clients default configuration.
	 * 
	 * @param pMethodName
	 *            The method being performed.
	 * @param pParams
	 *            The parameters.
	 * @param pCallback
	 *            The callback being notified when the request is finished.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public void executeAsync(String pMethodName, List pParams,
			AsyncCallback pCallback) throws XmlRpcException {
		executeAsync(getClientConfig(), pMethodName, pParams, pCallback);
	}

	/**
	 * Performs an asynchronous request with the given configuration.
	 * 
	 * @param pConfig
	 *            The request configuration.
	 * @param pMethodName
	 *            The method being performed.
	 * @param pParams
	 *            The parameters.
	 * @param pCallback
	 *            The callback being notified when the request is finished.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public void executeAsync(XmlRpcClientConfig pConfig, String pMethodName,
			List pParams, AsyncCallback pCallback) throws XmlRpcException {
		executeAsync(
				new XmlRpcClientRequestImpl(pConfig, pMethodName, pParams),
				pCallback);
	}

	/**
	 * Performs a request with the clients default configuration.
	 * 
	 * @param pRequest
	 *            The request being performed.
	 * @param pCallback
	 *            The callback being notified when the request is finished.
	 * @throws XmlRpcException
	 *             Performing the request failed.
	 */
	public void executeAsync(XmlRpcRequest pRequest, AsyncCallback pCallback)
			throws XmlRpcException {
		XmlRpcClientWorker w = (XmlRpcClientWorker) getWorkerFactory()
				.getWorker();
		w.execute(pRequest, pCallback);
	}

	/**
	 * Returns the clients instance of
	 * {@link org.apache.xmlrpc.serializer.XmlWriterFactory}.
	 * 
	 * @return A factory for creating instances of
	 *         {@link org.apache.ws.commons.serialize.XMLWriter}.
	 */
	public XmlWriterFactory getXmlWriterFactory() {
		return xmlWriterFactory;
	}

	/**
	 * Sets the clients instance of
	 * {@link org.apache.xmlrpc.serializer.XmlWriterFactory}.
	 * 
	 * @param pFactory
	 *            A factory for creating instances of
	 *            {@link org.apache.ws.commons.serialize.XMLWriter}.
	 */
	public void setXmlWriterFactory(XmlWriterFactory pFactory) {
		xmlWriterFactory = pFactory;
	}
}
